import wx
import os
import sys
import webbrowser
import keyword
import difflib
import tempfile
import shutil

import wx.combo
import wx.stc
import wx.html

if wx.Platform == "__WXMSW__":
    import wx.lib.iewin_old as iewin

import wx.lib.buttons as buttons
import wx.lib.hyperlink as hyper

from wx.lib.expando import ExpandoTextCtrl
from wx.lib.stattext import GenStaticText as StaticText
from wx.lib.wordwrap import wordwrap

import extern.ultimatelistctrl as ULC
import extern.listctrl as listmix
import extern.supertooltip as STT

from utilities import FormatTrace, EnvironmentInfo
from librarydescription import to_html, GetTopLevelParent

from constants import object_types, _treeActions, _outlookDummy, __version__


class BaseDialog(wx.Dialog):
    """ A wx.Dialog base class for all the other GUI2Exe dialogs. """

    def __init__(self, parent, size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE):
        """
        Default class constructor.

        
        **Parameters:**

        * parent: the dialog parent;
        """
            
        wx.Dialog.__init__(self, parent, size=size, style=style)
        self.MainFrame = parent


    def CreateButtons(self):
        """ Creates the Ok and cancel bitmap buttons. """
        
        # Build a couple of fancy and useless buttons        
        okBmp = self.MainFrame.CreateBitmap("ok")
        cancelBmp = self.MainFrame.CreateBitmap("file_error")
        self.okButton = buttons.ThemedGenBitmapTextButton(self, wx.ID_OK, okBmp, "Ok")
        self.cancelButton = buttons.ThemedGenBitmapTextButton(self, wx.ID_CANCEL, cancelBmp, "Cancel")


    def SetProperties(self, title):
        """ Sets few properties for the dialog. """        

        self.SetTitle(title)
        self.SetIcon(self.MainFrame.GetIcon())
        self.okButton.SetDefault()        


    def BindEvents(self):
        """ Binds the events to specific methods. """
        
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyUp)
        self.okButton.Bind(wx.EVT_BUTTON, self.OnOk)
        self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)


    def OnOk(self, event):
        """ Handles the Ok wx.EVT_BUTTON event for the dialog. """

        self.EndModal(wx.ID_OK)


    def OnCancel(self, event):
        """ Handles the Cancel wx.EVT_BUTTON event for the dialog. """

        self.OnClose(event)


    def OnClose(self, event):
        """ User canceled the dialog. """

        self.EndModal(wx.ID_CANCEL)


    def OnKeyUp(self, event):
        """ Handles the wx.EVT_CHAR_HOOK event for the dialog. """
        
        if event.GetKeyCode() == wx.WXK_ESCAPE:
            # Close the dialog, no action
            self.OnClose(event)
        elif event.GetKeyCode() in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
            # Close the dialog, the user wants to continue
            self.OnOk(event)

        event.Skip()


class VersionInfo(BaseDialog):

    def __init__(self, parent, locs):

        BaseDialog.__init__(self, parent)
        
        self.sizer_2_staticbox = wx.StaticBox(self, -1, "Namespace Diff Tool requirements")
        self.sizer_3_staticbox = wx.StaticBox(self, -1, "Your programmer")
        self.sizer_1_staticbox = wx.StaticBox(self, -1, "Namespace Diff Tool code base")

        self.loc1 = wx.StaticText(self, -1, "%d"%locs[0])
        self.loc2 = wx.StaticText(self, -1, "%d"%locs[1])
        self.loc3 = wx.StaticText(self, -1, "%d"%locs[2])
        self.loc4 = wx.StaticText(self, -1, "%d"%locs[3])

        self.lan1 = hyper.HyperLinkCtrl(self, -1, "Python", URL="http://www.python.org/")
        self.lan2 = hyper.HyperLinkCtrl(self, -1, "wxPython", URL="http://www.wxpython.org/")
        self.lan3 = hyper.HyperLinkCtrl(self, -1, "SQLAlchemy", URL="http://www.sqlalchemy.org/")
        
        self.phone = wx.StaticText(self, -1, "+45 3363 5963\n+45 5082 3282")
        self.mail1 = hyper.HyperLinkCtrl(self, -1, "andrea.gavana@gmail.com", URL="mailto:andrea.gavana@gmail.com")
        self.mail2 = hyper.HyperLinkCtrl(self, -1, "andrea.gavana@maerskoil.com", URL="andrea.gavana@maerskoil.com")
        self.web1 = hyper.HyperLinkCtrl(self, -1, "Infinity77", URL="http://xoomer.alice.it/infinity77/")

        self.CreateButtons()        
        self.SetProperties("Namespace Diff Tool version info")
        self.DoLayout()
        self.BindEvents()


    def DoLayout(self):

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        sizer_5 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_3 = wx.StaticBoxSizer(self.sizer_3_staticbox, wx.HORIZONTAL)
        bottomGrid = wx.FlexGridSizer(5, 2, 5, 5)
        sizer_4 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.StaticBoxSizer(self.sizer_2_staticbox, wx.HORIZONTAL)
        centerGrid = wx.FlexGridSizer(3, 2, 5, 5)
        sizer_1 = wx.StaticBoxSizer(self.sizer_1_staticbox, wx.HORIZONTAL)
        topGrid = wx.FlexGridSizer(4, 2, 5, 5)
        label_1 = wx.StaticText(self, -1, "This dialog gives some information about Namespace Diff Tool:")
        label_1.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        mainSizer.Add(label_1, 0, wx.ALL, 10)
        label_2 = wx.StaticText(self, -1, "Number of modules:")
        label_2.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        topGrid.Add(label_2, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        topGrid.Add(self.loc1, 0, wx.LEFT, 5)
        label_3 = wx.StaticText(self, -1, "Number of classes:")
        label_3.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        topGrid.Add(label_3, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        topGrid.Add(self.loc2, 0, wx.LEFT, 5)
        label_4 = wx.StaticText(self, -1, "Number of methods/functions:")
        label_4.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        topGrid.Add(label_4, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        topGrid.Add(self.loc3, 0, wx.LEFT, 5)
        label_5 = wx.StaticText(self, -1, "Lines of code:")
        label_5.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        topGrid.Add(label_5, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        topGrid.Add(self.loc4, 0, wx.LEFT, 5)
        sizer_1.Add(topGrid, 1, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(sizer_1, 0, wx.ALL|wx.EXPAND, 5)
        label_6 = wx.StaticText(self, -1, "Core language:")
        label_6.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        centerGrid.Add(label_6, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        centerGrid.Add(self.lan1, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        label_7 = wx.StaticText(self, -1, "User interface:")
        label_7.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        centerGrid.Add(label_7, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        centerGrid.Add(self.lan2, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        label_22 = wx.StaticText(self, -1, "Database handling:")
        label_22.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        centerGrid.Add(label_22, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        centerGrid.Add(self.lan3, 0, wx.ALIGN_CENTER_VERTICAL, 0)

        sizer_2.Add(centerGrid, 1, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(sizer_2, 0, wx.ALL|wx.EXPAND, 5)
        
        bmp = self.GetParent().CreateBitmap("andrea")
        gavana = wx.StaticBitmap(self, -1, bmp)
        
        sizer_3.Add(gavana, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5)
        sizer_3.Add((20, 20), 0, 0, 0)
        label_13 = wx.StaticText(self, -1, "Name:")
        label_13.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        bottomGrid.Add(label_13, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        label_18 = wx.StaticText(self, -1, "Andrea Gavana")
        bottomGrid.Add(label_18, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        label_14 = wx.StaticText(self, -1, "Address:")
        label_14.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        bottomGrid.Add(label_14, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        label_19 = wx.StaticText(self, -1, "Maersk Oil, Esplanaden, 1263 Copenhagen K, Denmark")
        bottomGrid.Add(label_19, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        label_15 = wx.StaticText(self, -1, "Phones:")
        label_15.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        bottomGrid.Add(label_15, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        bottomGrid.Add(self.phone, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        label_16 = wx.StaticText(self, -1, "E-Mails:")
        label_16.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        bottomGrid.Add(label_16, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_4.Add(self.mail1, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_4.Add(self.mail2, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        bottomGrid.Add(sizer_4, 1, wx.EXPAND, 0)
        label_17 = wx.StaticText(self, -1, "Web Page:")
        label_17.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
        bottomGrid.Add(label_17, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        bottomGrid.Add(self.web1, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_3.Add(bottomGrid, 1, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(sizer_3, 0, wx.ALL|wx.EXPAND, 5)
        sizer_5.Add(self.okButton, 0, wx.ALL, 15)
        sizer_5.Add((20, 20), 1, wx.EXPAND, 0)
        sizer_5.Add(self.cancelButton, 0, wx.ALL, 15)
        mainSizer.Add(sizer_5, 0, wx.EXPAND, 0)
        self.SetSizer(mainSizer)
        mainSizer.Fit(self)
        self.Layout()
        self.Centre()



def ExceptionHook(exctype, value, trace):
    """
    Handler for all unhandled exceptions.

    
    **Parameters:**

    * exctype: Exception Type
    * value: Error Value
    * trace: Trace back info
    
    **Note:**

    *  from Editra.dev_tool
    """
    ftrace = FormatTrace(exctype, value, trace)

    # Ensure that error gets raised to console as well
    print ftrace

    if not ErrorDialog.REPORTER_ACTIVE:
        ErrorDialog(ftrace)


class ErrorReporter(object):
    """Crash/Error Reporter Service
    @summary: Stores all errors caught during the current session and
    is implemented as a singleton so that all errors pushed
    onto it are kept in one central location no matter where
    the object is called from.
    
    **Note:**

    * from Editra.dev_tool

    """
    instance = None
    _first = True
    def __init__(self):
        """Initialize the reporter
        
        **Note:**

        * The ErrorReporter is a singleton.

        """
        # Ensure init only happens once
        if self._first:
            object.__init__(self)
            self._first = False
            self._sessionerr = list()
        else:
            pass

    def __new__(cls, *args, **kargs):
        """Maintain only a single instance of this object
        
        **Returns:**

        * instance of this class

        """
        if not cls.instance:
            cls.instance = object.__new__(cls, *args, **kargs)
        return cls.instance

    def AddMessage(self, msg):
        """Adds a message to the reporters list of session errors
        
        **Parameters:**

        * msg: The Error Message to save

        """
        if msg not in self._sessionerr:
            self._sessionerr.append(msg)

    def GetErrorStack(self):
        """Returns all the errors caught during this session
        
        **Returns:**

        * formatted log message of errors

        """
        return "\n\n".join(self._sessionerr)

    def GetLastError(self):
        """Gets the last error from the current session
        
        **Returns:**

        * Error Message String

        """
        if len(self._sessionerr):
            return self._sessionerr[-1]



ID_SEND = wx.NewId()
class ErrorDialog(BaseDialog):
    """
    Dialog for showing errors and and notifying gui2exe-users should the
    user choose so.
    
    **Note:**

    * partially from Editra.dev_tool
    """
    ABORT = False
    REPORTER_ACTIVE = False
    def __init__(self, message):
        """
        Initialize the dialog
        
        **Parameters:**

        * message: Error message to display
        """
        ErrorDialog.REPORTER_ACTIVE = True

        # Get version from the app since the main window may be dead or
        # not even ready yet.

        app = wx.GetApp()        
        version = app.GetVersion()
        
        size = app.GetPreference("ERROR_DIALOG_SIZE", default=wx.DefaultSize)

        if size[0] < 50 or size[1] < 50:
            size = wx.DefaultSize
        
        BaseDialog.__init__(self, app.GetTopWindow(), size=size, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
        
        # Give message to ErrorReporter
        ErrorReporter().AddMessage(message)

        self.SetIcon(self.MainFrame.GetIcon())
        self.SetTitle("Namespace Diff Tool Error/Crash Reporter")

        # Attributes
        self.err_msg = "%s\n\n%s\n%s\n%s" % (EnvironmentInfo(__version__), \
                                             "#---- Traceback Info ----#", \
                                             ErrorReporter().GetErrorStack(), \
                                             "#---- End Traceback Info ----#")

        errorBmp = self.MainFrame.CreateBitmap("bug")
        abortBmp = self.MainFrame.CreateBitmap("abort")
        sendBmp = self.MainFrame.CreateBitmap("send")
        cancelBmp = self.MainFrame.CreateBitmap("exit")

        self.errorBmp = wx.StaticBitmap(self, -1, errorBmp)
        
        self.textCtrl = wx.TextCtrl(self, value=self.err_msg, style=wx.TE_MULTILINE)
        
        self.abortButton = buttons.ThemedGenBitmapTextButton(self, wx.ID_ABORT, abortBmp, "Abort", size=(-1, 26))
        self.sendButton = buttons.ThemedGenBitmapTextButton(self, ID_SEND, sendBmp, "Report error", size=(-1, 26))
        self.sendButton.SetDefault()
        self.closeButton = buttons.ThemedGenBitmapTextButton(self, wx.ID_CLOSE, cancelBmp, "Close", size=(-1, 26))

        # Layout
        self.DoLayout()

        # Event Handlers
        self.Bind(wx.EVT_BUTTON, self.OnButton)
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.textCtrl.RemoveSelection()
        self.sendButton.SetFocus()
        
        # Auto show at end of init
        self.CenterOnParent()
        self.ShowModal()


    def DoLayout(self):
        """
        Layout the dialog and prepare it to be shown
        
        
        **Note:**

        *  Do not call this method in your code
        """

        # Objects
        mainmsg = wx.StaticText(self, 
                                label="Error: Oh no, something bad happened!\n\n" \
                                'Help improve Namespace Diff Tool by clicking on "Report Error" button to send\nthe error traceback shown below.')
        
        t_lbl = wx.StaticText(self, label="Error traceback:")

        t_lbl.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.BOLD, False))
        # Layout
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        topSizer = wx.BoxSizer(wx.HORIZONTAL)
        bottomSizer = wx.BoxSizer(wx.HORIZONTAL)

        topSizer.Add(self.errorBmp, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER, 20)
        topSizer.Add(mainmsg, 0, wx.EXPAND|wx.RIGHT, 20)
        mainSizer.Add(topSizer, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 20)
        mainSizer.Add(t_lbl, 0, wx.LEFT|wx.TOP|wx.RIGHT, 5)
        mainSizer.Add((0, 2))
        mainSizer.Add(self.textCtrl, 2, wx.EXPAND|wx.LEFT|wx.BOTTOM|wx.RIGHT, 5)
        bottomSizer.Add(self.abortButton, 0, wx.ALL, 5)
        bottomSizer.Add((0, 0), 1, wx.EXPAND)
        bottomSizer.Add(self.sendButton, 0, wx.TOP|wx.BOTTOM, 5)
        bottomSizer.Add((0, 10))
        bottomSizer.Add(self.closeButton, 0, wx.TOP|wx.BOTTOM|wx.RIGHT, 5)
        mainSizer.Add(bottomSizer, 0, wx.EXPAND)

        self.SetSizer(mainSizer)
        mainSizer.Layout()
        
        self.Fit()
        

    def OnButton(self, evt):
        """Handles button events
        
        **Parameters:**

        * evt: event that called this handler

        **Post-Conditions:**
        
        * Dialog is closed
        * If Report Event then email program is opened

        """

        wx.GetApp().SetPreference("ERROR_DIALOG_SIZE", self.GetSize())
        
        e_id = evt.GetId()
        if e_id == wx.ID_CLOSE:
            self.Close()
        elif e_id == ID_SEND:
            msg = 'mailto:%s?subject=NDT%%20error%%20report&body="%s"'
            addr = "andrea.gavana@gmail.com"
            err_msg = self.err_msg

            if "__WXMSW__" in wx.Platform:
                for key, value in _outlookDummy.items():
                    err_msg = err_msg.replace(key, value)
            else:
                msg = msg.replace("%%20", " ")

            msg = msg % (addr, err_msg)
            msg = msg.replace("'", '')
            msg = msg.replace('"', "")

            if "__WXMSW__" in wx.Platform:
                msg = msg.replace("\n", "%0a")

            webbrowser.open(msg)
            self.Close()
        elif e_id == wx.ID_ABORT:
            ErrorDialog.ABORT = True
            # Try a nice shutdown first time through
            wx.CallLater(500, wx.GetApp().OnExit, 
                         wx.MenuEvent(wx.wxEVT_MENU_OPEN, wx.ID_EXIT),
                         True)
            self.Close()
        else:
            evt.Skip()

    def OnClose(self, evt):
        """Cleans up the dialog when it is closed
        
        **Parameters:**

        * evt: Event that called this handler

        """
        ErrorDialog.REPORTER_ACTIVE = False
        self.Destroy()
        evt.Skip()


class BitmapFilePicker(wx.Panel):

    def __init__(self, parent, path="", wildcard="All files (*.*)|*.*"):

        wx.Panel.__init__(self, parent, style=wx.NO_BORDER|
                              wx.NO_FULL_REPAINT_ON_RESIZE | wx.CLIP_CHILDREN)

        self.MainFrame = wx.GetTopLevelParent(self)
        
        self.wildcard = wildcard
        self.path = path

        browseBmp = self.MainFrame.CreateBitmap("browse")
        self.textCtrl = wx.TextCtrl(self, -1, path, style=wx.TE_NOHIDESEL | wx.TE_PROCESS_ENTER)
        self.button = buttons.ThemedGenBitmapTextButton(self, -1, browseBmp, " Browse")

        self.button.SetUseFocusIndicator(False)
        
        best = self.DoGetBestSize()
        buttonBest = self.button.DoGetBestSize()
        self.button.SetInitialSize((buttonBest[0]-4, best.y+1))
        self.SetInitialSize(best)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.textCtrl, 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL)
        sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, wx.SizerFlags().Border().GetBorderInPixels())

        self.SetSizer(sizer)
        sizer.Layout()

        self.button.Bind(wx.EVT_BUTTON, self.OnButton)
        self.textCtrl.Bind(wx.EVT_TEXT, self.OnText)
        self.textCtrl.Bind(wx.EVT_TEXT_PASTE, self.OnText)


    def DoGetBestSize(self):

        return wx.Size(-1, self.textCtrl.GetBestSize().y)
    

    def GetPath(self):

        return self.textCtrl.GetValue()


    def SetPath(self, value):

        self.textCtrl.ChangeValue(value)


    def OnButton(self, event):

        event.Skip()
        
        dlg = wx.FileDialog(self, "Select a file", defaultFile=self.path, wildcard=self.wildcard,
                            style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.textCtrl.ChangeValue(path)
            self.SendEvent(path)

        dlg.Destroy()


    def OnText(self, event):

        event.Skip()
        self.SendEvent(self.textCtrl.GetValue())


    def SetValue(self, value):

        self.textCtrl.ChangeValue(value)
        self.SendEvent(value)
        

    def SendEvent(self, path):

        event = wx.FileDirPickerEvent(wx.wxEVT_COMMAND_FILEPICKER_CHANGED, self, self.GetId(), path)

        if "2.9" in wx.VERSION_STRING:
            self.ProcessWindowEvent(event)
        else:
            self.GetEventHandler().ProcessEvent(event)
        

class InputComboBox(wx.combo.OwnerDrawnComboBox):

    def __init__(self, parent, style=0):

        wx.combo.OwnerDrawnComboBox.__init__(self, parent, style=style)
        self.MainFrame = wx.GetTopLevelParent(self)


    def DrawSelection(self, dc, rect):

        # Windows Vista Colours
        outer = wx.Colour(170, 200, 245)
        inner = wx.Colour(230, 250, 250)
        top = wx.Colour(210, 240, 250)
        bottom = wx.Colour(185, 215, 250)

        bdrRect = wx.Rect(*rect.Get())
        filRect = wx.Rect(*rect.Get())
        filRect.Deflate(1,1)
        
        r1, g1, b1 = int(top.Red()), int(top.Green()), int(top.Blue())
        r2, g2, b2 = int(bottom.Red()), int(bottom.Green()), int(bottom.Blue())

        flrect = float(filRect.height)
        if flrect < 1:
            flrect = self._lineHeight

        rstep = float((r2 - r1)) / flrect
        gstep = float((g2 - g1)) / flrect
        bstep = float((b2 - b1)) / flrect

        rf, gf, bf = 0, 0, 0
        dc.SetPen(wx.TRANSPARENT_PEN)
        
        for y in xrange(filRect.y, filRect.y + filRect.height):
            currCol = (r1 + rf, g1 + gf, b1 + bf)
            dc.SetBrush(wx.Brush(currCol, wx.SOLID))
            dc.DrawRectangle(filRect.x, y, filRect.width, 1)
            rf = rf + rstep
            gf = gf + gstep
            bf = bf + bstep
        
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(wx.Pen(outer))
        dc.DrawRoundedRectangleRect(bdrRect, 3)
        bdrRect.Deflate(1, 1)
        dc.SetPen(wx.Pen(inner))
        dc.DrawRoundedRectangleRect(bdrRect, 2)

        
    # Overridden from OwnerDrawnComboBox, called to draw each
    # item in the list
    def OnDrawItem(self, dc, rect, item, flags):
        
        if item == wx.NOT_FOUND:
            # painting the control, but there is no valid item selected yet
            return

        if flags & wx.combo.ODCB_PAINTING_SELECTED:
            self.DrawSelection(dc, rect)
            
        r = wx.Rect(*rect)  # make a copy
        r.Deflate(3, 5)

        string = self.GetString(item)
        index = self.GetItems().index(string)
        
        # for painting the items in the popup
        dc.SetTextForeground(wx.BLACK)

        bmp = self.GetClientData(index)
        dc.DrawImageLabel(string, bmp, r, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
        

    # Overridden from OwnerDrawnComboBox, should return the height
    # needed to display an item in the popup, or -1 for default
    def OnMeasureItem(self, item):
        # Simply demonstrate the ability to have variable-height items
        return 24

    # Overridden from OwnerDrawnComboBox.  Callback for item width, or
    # -1 for default/undetermined
    def OnMeasureItemWidth(self, item):

        dc = wx.ClientDC(self)
        string = self.GetString(item)

        return dc.GetTextExtent(string)[0] + 25


class DocstringsSTC(wx.stc.StyledTextCtrl):

    fold_symbols = 2
    
    def __init__(self, parent):
        
        wx.stc.StyledTextCtrl.__init__(self, parent, style=wx.BORDER_THEME)

        self.SetMinSize((-1, 200))

        self.CmdKeyAssign(ord('B'), wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMIN)
        self.CmdKeyAssign(ord('N'), wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMOUT)

        self.SetLexer(wx.stc.STC_LEX_PYTHON)
        self.SetKeyWords(0, " ".join(keyword.kwlist))

        # Enable folding
        self.SetProperty("fold", "1" ) 

        # Highlight tab/space mixing (shouldn't be any)
        self.SetProperty("tab.timmy.whinge.level", "1")

        # Set left and right margins
        self.SetMargins(2, 2)

        # Set up the numbers in the margin for margin #1
        self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
        # Reasonable value for, say, 4-5 digits using a mono font (40 pix)
        self.SetMarginWidth(1, 30)

        # Indentation and tab stuff
        self.SetIndent(4)               # Proscribed indent size for wx
        self.SetIndentationGuides(True) # Show indent guides
        self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
        self.SetTabIndents(True)        # Tab key indents
        self.SetTabWidth(4)             # Proscribed tab size for wx
        self.SetUseTabs(False)          # Use spaces rather than tabs, or
                                        # TabTimmy will complain!    
        # White space
        self.SetViewWhiteSpace(False)   # Don't view white space

        # EOL: Since we are loading/saving ourselves, and the
        # strings will always have \n's in them, set the STC to
        # edit them that way.            
        self.SetEOLMode(wx.stc.STC_EOL_LF)
        self.SetViewEOL(False)
        
        # No right-edge mode indicator
        self.SetEdgeMode(wx.stc.STC_EDGE_NONE)

        # Global default style
        if wx.Platform == '__WXMSW__':
            self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, 
                              'fore:#000000,back:#FFFFFF,face:Courier New')
        elif wx.Platform == '__WXMAC__':
            # TODO: if this looks fine on Linux too, remove the Mac-specific case 
            # and use this whenever OS != MSW.
            self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, 
                              'fore:#000000,back:#FFFFFF,face:Monaco')
        else:
            defsize = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT).GetPointSize()
            self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, 
                              'fore:#000000,back:#FFFFFF,face:Courier,size:%d'%defsize)

        # Clear styles and revert to default.
        self.StyleClearAll()

        # Following style specs only indicate differences from default.
        # The rest remains unchanged.

        # Line numbers in margin
        self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2')    
        # Highlighted brace
        self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
        # Unmatched brace
        self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
        # Indentation guide
        self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")

        # Python styles
        self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
        # Comments
        self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE,  'fore:#008000')
        self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#C0C0C0')
        # Numbers
        self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
        # Strings and characters
        self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#808000')
        self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#808000')
        # Keywords
        self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
        # Triple quotes
        self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080')
        self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080')
        # Class names
        self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
        # Function names
        self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
        # Operators
        self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
        # Identifiers. I leave this as not bold because everything seems
        # to be an identifier if it doesn't match the above criterae
        self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')

        # Caret color
        self.SetCaretForeground("BLUE")
        # Selection background
        self.SetSelBackground(1, '#66CCFF')

        self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
        self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))

        self.SetWrapMode(wx.stc.STC_WRAP_WORD)
        self.SetWrapVisualFlags(wx.stc.STC_WRAPVISUALFLAG_END)
        
##        self.SetUseHorizontalScrollBar(False)


class MyExpandoTextCtrl(ExpandoTextCtrl):
    """ Adds self-adjustment on size events to `ExpandoTextCtrl`. """
    
    def __init__(self, *args, **kw):
        """ Default class constructor. See `wx.lib.expando.ExpandoTextCtrl` for more information. """

        ExpandoTextCtrl.__init__(self, *args, **kw)
        self.Bind(wx.EVT_SIZE, self.OnSize)


    def OnSize(self, event):
        """
        Handles the ``wx.EVT_SIZE`` event for `MyExpandoTextCtrl`.

        :param `event`: a `wx.SizeEvent`.
        """

        self._adjustCtrl()
        event.Skip()


class StaticTextImage(wx.PyControl):

    def __init__(self, parent, text, bitmap, backColour=wx.NullColour, hasGradient=False,
                 style=wx.NO_BORDER):

        wx.PyControl.__init__(self, parent, style=style)

        self.text = text
        self.bitmap = bitmap

        if not backColour.IsOk():
            backColour = parent.GetBackgroundColour()
            
        self.backColour = backColour
        self.hasGradient = hasGradient

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)


    def DoGetBestSize(self):
        """
        Overridden base class virtual.  Determines the best size of the control
        based on the label size, the bitmap size and the current font.
        """

        # Retrieve our properties: the text label, the font and the check
        # bitmap
        label = self.text
        bitmap = self.bitmap
        font = self.GetFont()

        # Set up a wx.ClientDC. When you don't have a dc available (almost
        # always you don't have it if you are not inside a wx.EVT_PAINT event),
        # use a wx.ClientDC (or a wx.MemoryDC) to measure text extents
        dc = wx.ClientDC(self)
        dc.SetFont(font)

        # Measure our label
        textWidth, textHeight, descent, externalLeading = dc.GetFullTextExtent(label)

        spacing = bitmapWidth = bitmapHeight = 0

        if bitmap.IsOk():
            # Retrieve the check bitmap dimensions
            bitmapWidth, bitmapHeight = bitmap.GetWidth(), bitmap.GetHeight()

            # Get the spacing between the check bitmap and the text
            spacing = 3
            diff = 0
        else:
            diff = 2

        # Ok, we're almost done: the total width of the control is simply
        # the sum of the bitmap width, the spacing and the text width,
        # while the height is the maximum value between the text width and
        # the bitmap width
        totalWidth = bitmapWidth + spacing + textWidth + 10
        totalHeight = max(textHeight+diff+descent, bitmapHeight)

        if self.GetWindowStyle() & wx.BORDER_THEME:
            totalHeight += 6
            
        best = wx.Size(totalWidth, totalHeight)

        # Cache the best size so it doesn't need to be calculated again,
        # at least until some properties of the window change
        self.CacheBestSize(best)

        return best

    
    def OnPaint(self, event):
        """ Handles the wx.EVT_PAINT event for L{StaticTextImage}. """

        # If you want to reduce flicker, a good starting point is to
        # use wx.BufferedPaintDC.
        dc = wx.BufferedPaintDC(self)

        # It is advisable that you don't overcrowd the OnPaint event
        # (or any other event) with a lot of code, so let's do the
        # actual drawing in the Draw() method, passing the newly
        # initialized wx.BufferedPaintDC
        self.Draw(dc)


    def Draw(self, dc):
        """
        Actually performs the drawing operations, for the bitmap and
        for the text, positioning them centered vertically.
        """

        # Get the actual client size of ourselves
        width, height = self.GetClientSize()

        if width < 5 or height < 5:
            # Nothing to do, we still don't have dimensions!
            return

        # Initialize the wx.BufferedPaintDC, assigning a background
        # colour and a foreground colour (to draw the text)
        backBrush = wx.Brush(self.backColour, wx.SOLID)
        dc.SetBackground(backBrush)
        dc.Clear()

        if self.IsEnabled():
            dc.SetTextForeground(self.GetForegroundColour())
        else:
            dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))

        dc.SetFont(self.GetFont())

        # Get the text label for the checkbox, the associated check bitmap
        # and the spacing between the check bitmap and the text
        label = self.text
        bitmap = self.bitmap
        spacing = 3

        # Measure the text extent and get the check bitmap dimensions
        textWidth, textHeight = dc.GetTextExtent(label)

        spacing = bitmapWidth = bitmapHeight = 0

        diff = 0
        if self.GetWindowStyle() & wx.BORDER_THEME:
            diff = 3

        if bitmap.IsOk():
            bitmapWidth, bitmapHeight = bitmap.GetWidth(), bitmap.GetHeight()
            spacing = 3

            # Position the bitmap centered vertically
            bitmapXpos = diff
            bitmapYpos = (height - bitmapHeight)/2

            # Position the text centered vertically
            textXpos = bitmapWidth + spacing + diff

            # Draw the bitmap on the DC
            dc.DrawBitmap(bitmap, bitmapXpos, bitmapYpos, True)

        else:
            textXpos = diff
            
        textYpos = (height - textHeight)/2

        # Draw the text
        dc.DrawText(label, textXpos, textYpos)


class StaticHeader(wx.PyControl):

    def __init__(self, parent, text, bitmap):

        wx.PyControl.__init__(self, parent, style=wx.NO_BORDER)

        self.text = text
        self.bitmap = bitmap
        self.enter = False

        self.Bind(wx.EVT_PAINT, self.OnPaint)	
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
        self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow)
        self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)


    def DoGetBestSize(self):
        """
        Overridden base class virtual.  Determines the best size of the control
        based on the label size, the bitmap size and the current font.
        """

        w, h, d, dummy = self.GetFullTextExtent("Hg")
        nativeH = wx.RendererNative.Get().GetHeaderButtonHeight(self.GetParent())

        maxH = max(h, nativeH)
        maxH += d

        bitmapWidth = 0

        if self.bitmap.IsOk():
            bitmapWidth, bitmapHeight = self.bitmap.GetWidth(), self.bitmap.GetHeight()
            maxH = max(maxH, bitmapHeight+6)
            
        textWidth, textHeight = self.GetTextExtent(self.text)
        totalWidth = bitmapWidth + 3 + textWidth + 10
        
        best = wx.Size(totalWidth, maxH)
        # Cache the best size so it doesn't need to be calculated again,
        # at least until some properties of the window change
        self.CacheBestSize(best)

        return best

    
    def OnPaint(self, event):
        """ Handles the wx.EVT_PAINT event for L{StaticHeader}. """

        dc = wx.BufferedPaintDC(self)
        # width and height of the entire header window
        w, h = self.GetClientSize()
        dc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)))
        dc.SetPen(wx.TRANSPARENT_PEN)
        dc.DrawRectangle(0, -1, w, h+2)
        
        dc.SetBackgroundMode(wx.TRANSPARENT)
        dc.SetTextForeground(self.GetForegroundColour())

        flags = 0
        renderer = wx.RendererNative.Get()

        if self.enter:
            flags |= wx.CONTROL_CURRENT
       
        # the width of the rect to draw: make it smaller to fit entirely
        # inside the column rect
        header_rect = wx.Rect(1, 0, w+1, h)
        renderer.DrawHeaderButton(self, dc, header_rect, flags)

        textXpos = 5
        bmpWidth = bmpHeight = 0
        
        if self.bitmap.IsOk():
            bmpWidth, bmpHeight = self.bitmap.GetWidth(), self.bitmap.GetHeight()
            textXpos += bmpWidth + 3

            bitmapXpos = 5
            bitmapYpos = (h - bmpHeight)/2 - 1

            # Draw the bitmap on the DC
            dc.DrawBitmap(self.bitmap, bitmapXpos, bitmapYpos, True)

        dc.SetFont(self.GetFont())
        textWidth, textHeight = dc.GetTextExtent(self.text)
        
        # Position the text centered vertically
        textYpos = (h - textHeight)/2 - 1

        # Draw the text
        dc.DrawText(self.text, textXpos, textYpos)


    def OnEnterWindow(self, event):
        """
        Handles the ``wx.EVT_ENTER_WINDOW`` event for L{StaticHeader}.

        :param `event`: a `wx.MouseEvent` event to be processed.
        """

        self.enter = True
        self.Refresh()


    def OnLeaveWindow(self, event):
        """
        Handles the ``wx.EVT_LEAVE_WINDOW`` event for L{StaticHeader}.

        :param `event`: a `wx.MouseEvent` event to be processed.
        """

        self.enter = False
        self.Refresh()

            
class NotebookWidget(wx.Panel):

    def __init__(self, parent, numColumns, numRows=2):
        
##        wx.PyPanel.__init__(self, parent, size=(0, 0), style=wx.TAB_TRAVERSAL|wx.BORDER_THEME)

        if '2.9' in wx.VERSION_STRING:
            # I will kill that guy who messed up with sizers in 2.9
            wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL|wx.BORDER_THEME)
        else:
            wx.Panel.__init__(self, parent, size=(0, 0), style=wx.TAB_TRAVERSAL|wx.BORDER_THEME)

        self.Hide()        

        self.SetBackgroundColour(wx.WHITE)
        boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        boldFont.SetWeight(wx.BOLD)

        self.boldFont = boldFont
        self.backColour = self.GetBackgroundColour()

        self.flexSizer = wx.FlexGridSizer(rows=numRows, cols=numColumns, vgap=5, hgap=0)
        self.columnCount = 0
        self.growableCols = []
        self.numColumns = numColumns
        

    def DoGetBestSize(self):

        static = self.flexSizer.GetItem(0)
        size1 = static.GetWindow().GetEffectiveMinSize()

        y1 = size1.height
        y2 = 0
        
        for win in self.GetChildren():
            if isinstance(win, ExpandoTextCtrl) or isinstance(win, StaticTextImage):
                size = win.GetEffectiveMinSize()             
                y2 = max(y2, size.y)
            
        best = wx.Size(-1, y1+y2+10)
        self.CacheBestSize(best)

        return best
        
        
    def AddHeader(self, value, bitmap=wx.NullBitmap):
        
        st = StaticHeader(self, value, bitmap)
        st.SetFont(self.boldFont)
        
        self.flexSizer.Add(st, 1, wx.EXPAND)


    def Add(self, value, bitmap, resizable):

        if resizable:

            if bitmap.IsOk():
                bmp = wx.StaticBitmap(self, -1, bitmap)

            widget = MyExpandoTextCtrl(self, -1, value, style=wx.NO_BORDER|wx.TE_READONLY)
            expand = wx.EXPAND
            align1 = wx.LEFT|wx.ALIGN_TOP
            align2 = wx.ALIGN_BOTTOM|wx.TOP|wx.LEFT
            space = 1
            
            self.growableCols.append(self.columnCount)

        else:

            widget = StaticTextImage(self, value, bitmap, self.backColour)
            expand = 0
            align1 = wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL
            align2 = wx.ALIGN_CENTER_VERTICAL|wx.LEFT
            space = 3
        
        subSizer = wx.BoxSizer(wx.HORIZONTAL)

        if resizable and bitmap.IsOk():
            subSizer.Add(bmp, 0, align1, 3)
            
        subSizer.Add(widget, 1, align2, space)
        self.flexSizer.Add(subSizer, 1, wx.ALIGN_TOP|expand|wx.LEFT, 3)
        self.columnCount += 1

        return widget        


    def Finish(self):

        for column in self.growableCols:
            if column >= self.numColumns:
                break
            self.flexSizer.AddGrowableCol(column)
            
        self.SetSizer(self.flexSizer)
        
        self.Bind(wx.EVT_PAINT, self.OnPaint)	
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
        self.Bind(wx.EVT_SIZE, self.OnSize)

        self.Show()
        
        del self.growableCols, self.columnCount        
                

    def OnSize(self, event):
        """
        Handles the ``wx.EVT_SIZE`` event for `NotebookPage`.

        :param `event`: a `wx.SizeEvent`.
        """

        event.Skip()
        self.Refresh()


    def OnPaint(self, event):
        """
        Handles the ``wx.EVT_PAINT`` event for `NotebookPage`.

        :param `event`: a `wx.PaintEvent`.
        """

        dc = wx.BufferedPaintDC(self)
        dc.Clear()

        rowPos = colPos = 0

        rowHeights = self.flexSizer.GetRowHeights()
        colWidths = self.flexSizer.GetColWidths()

        size = self.GetClientSize()
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(wx.Pen(wx.Colour(210, 210, 210)))

        for rowH in rowHeights:
            if rowPos == 0:
                rowPos += rowH
                continue

            rowPos += rowH + 5
            dc.DrawLine(0, rowPos, size.x, rowPos)

        lenCols = len(colWidths)
        for indx, colW in enumerate(colWidths):
            if indx == lenCols-1:
                break
            colPos += colW
            dc.DrawLine(colPos, 0, colPos, rowPos)


    def OnErase(self, event):
        """
        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for `NotebookPage`.

        :param `event`: a `wx.EraseEvent`.
        """

        # This is intentionally empty, to reduce flicker.        
        pass


class AutoWrapStaticText(StaticText):

    def __init__(self, parent, label):

        self.label = label
        StaticText.__init__(self, parent, -1, label, style=wx.ST_NO_AUTORESIZE)

        self.Bind(wx.EVT_SIZE, self.OnSize)


    def OnSize(self, event):

        event.Skip()

        self.Wrap(event.GetSize().width)


    def Wrap(self, width):

        self.Freeze()

        dc = wx.ClientDC(self)
        dc.SetFont(self.GetFont())
        text = wordwrap(self.label, width, dc)
        self.SetLabel(text)

        self.Thaw()
        

class LibraryFrame(wx.Frame, listmix.ColumnSorterMixin):

    def __init__(self, parent, allDB, dbInfo):

        wx.Frame.__init__(self, parent, -1, "NDT Libraries Information", size=(800, 700), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_NO_TASKBAR|wx.FRAME_FLOAT_ON_PARENT)

        self.MainFrame = parent
        self.inDialog = False
        
        panel = wx.Panel(self)

        msg = "This window contains general information about the NDT database, "
        msg += "more detailed information about each library saved in the database "
        msg += "and allows you to delete library trees stored in it."
        
        self.caption = AutoWrapStaticText(panel, msg)
        self.location = wx.StaticText(panel, -1, "")
        self.size = wx.StaticText(panel, -1, "")
        self.libraries = wx.StaticText(panel, -1, "")
        self.modified = wx.StaticText(panel, -1, "")
        self.topSizer_staticbox = wx.StaticBox(panel, -1, "General information")
        self.libraryList = ULC.UltimateListCtrl(panel, style=wx.SUNKEN_BORDER,
                                                agwStyle=wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES|ULC.ULC_HOT_TRACKING|
                                                ULC.ULC_HAS_VARIABLE_ROW_HEIGHT|ULC.ULC_SHOW_TOOLTIPS)
        self.middleSizer_staticbox = wx.StaticBox(panel, -1, "Libraries")

        deleteBmp = self.MainFrame.CreateBitmap("database_delete")
        closeBmp = self.MainFrame.CreateBitmap("file_error")
        exportBmp = self.MainFrame.CreateBitmap("export")

        self.idExport = wx.NewId()        
        self.deleteButton = buttons.ThemedGenBitmapTextButton(panel, wx.ID_DELETE, deleteBmp, " Delete selected ")
        self.exportButton = buttons.ThemedGenBitmapTextButton(panel, self.idExport, exportBmp, " Export table ")
        self.closeButton = buttons.ThemedGenBitmapTextButton(panel, wx.ID_CANCEL, closeBmp, " Cancel ")
        
        self.panel = panel

        self.SetProperties(allDB, dbInfo)        
        self.DoLayout()

        self.Bind(wx.EVT_BUTTON, self.OnDelete, self.deleteButton)
        self.Bind(wx.EVT_BUTTON, self.OnClose, self.closeButton)
        self.Bind(wx.EVT_BUTTON, self.OnExport, self.exportButton)
        
        self.deleteButton.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI)
        self.Bind(wx.EVT_CHAR_HOOK, self.OnCharHook)

        self.CenterOnParent()
        self.Show()

        wx.CallAfter(panel.Layout)


    def SetProperties(self, allDB, dbInfo):

        self.location.SetLabel(dbInfo[0])
        self.size.SetLabel(dbInfo[1])
        self.libraries.SetLabel("%d"%dbInfo[2])
        self.modified.SetLabel("%s"%dbInfo[3])

        self.SetIcon(self.MainFrame.GetIcon())

        il = wx.ImageList(16, 16)

        self.sm_up = il.Add(self.MainFrame.CreateBitmap("sort_up"))
        self.sm_dn = il.Add(self.MainFrame.CreateBitmap("sort_down"))

        columns = ["Library", "Location", "Python version", "Descendants", "Scan date", "Size on disk (kb)"]
        
        for indx, colName in enumerate(columns):
            il.Add(self.MainFrame.CreateBitmap(colName.split()[0].lower()))
            info = ULC.UltimateListItem()
            info._mask = wx.LIST_MASK_TEXT
            info._text = colName + 3*" "
            self.libraryList.InsertColumnInfo(indx, info)
            
        count = len(columns) + 2
        key = 1
        itemDataMap = {}
        lendb = len(allDB)
        
        for name, filename, python, num_children, scan_date, icon, size in allDB:

            il.Add(self.MainFrame.CreateBitmap(icon))
            index = self.libraryList.InsertImageStringItem(sys.maxint, "%s"%name, count)

            self.libraryList.SetStringItem(index, 1, "%s"%os.path.split(filename)[0], 3)
            self.libraryList.SetStringItem(index, 2, "%s"%python, 4)
            self.libraryList.SetStringItem(index, 3, "%d"%num_children, 5)
            self.libraryList.SetStringItem(index, 4, "%s"%scan_date, 6)
            self.libraryList.SetStringItem(index, 5, "%d"%size, 7)
            self.libraryList.SetItemData(index, key)

            itemDataMap[key] = (name, filename, python, num_children, scan_date, size)
            key += 1
            count += 1

        for indx, colName in enumerate(columns):
            if "Size" in colName or "Descendants" in colName or "Python version" in colName or lendb == 0:
                self.libraryList.SetColumnWidth(indx, wx.LIST_AUTOSIZE_USEHEADER)
            elif indx == 1:
                self.libraryList.SetColumnWidth(indx, ULC.ULC_AUTOSIZE_FILL)
            else:
                self.libraryList.SetColumnWidth(indx, wx.LIST_AUTOSIZE)

        self.libraryList.AssignImageList(il, wx.IMAGE_LIST_SMALL)
        self.itemDataMap = itemDataMap
        listmix.ColumnSorterMixin.__init__(self, len(columns))

        self.libraryList.EnableSelectionVista()
        self.SortListItems(0, True)


    def DoLayout(self):

        boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        boldFont.SetWeight(wx.BOLD)

        self.caption.SetFont(boldFont)

        frameSizer = wx.BoxSizer(wx.VERTICAL)

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        bottomSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.middleSizer_staticbox.Lower()
        middleSizer = wx.StaticBoxSizer(self.middleSizer_staticbox, wx.HORIZONTAL)
        self.topSizer_staticbox.Lower()
        topSizer = wx.StaticBoxSizer(self.topSizer_staticbox, wx.HORIZONTAL)
        flexSizer = wx.FlexGridSizer(4, 2, 5, 5)

        mainSizer.Add((0, 5))
        mainSizer.Add(self.caption, 0, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add((0, 5))

        label_1 = wx.StaticText(self.panel, -1, "Database location:")
        label_1.SetFont(boldFont)
        flexSizer.Add(label_1, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        flexSizer.Add(self.location, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        label_2 = wx.StaticText(self.panel, -1, "Database size:")
        label_2.SetFont(boldFont)
        flexSizer.Add(label_2, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        flexSizer.Add(self.size, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        label_3 = wx.StaticText(self.panel, -1, "Number of libraries:")
        label_3.SetFont(boldFont)
        flexSizer.Add(label_3, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        flexSizer.Add(self.libraries, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        label_4 = wx.StaticText(self.panel, -1, "Last modified:")
        label_4.SetFont(boldFont)
        flexSizer.Add(label_4, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        flexSizer.Add(self.modified, 1, wx.ALIGN_CENTER_VERTICAL, 0)

        flexSizer.AddGrowableCol(1)
        topSizer.Add(flexSizer, 1, wx.EXPAND, 0)
        mainSizer.Add(topSizer, 0, wx.ALL|wx.EXPAND, 5)
        middleSizer.Add(self.libraryList, 1, wx.EXPAND, 0)
        mainSizer.Add(middleSizer, 1, wx.ALL|wx.EXPAND, 5)
        bottomSizer.Add(self.deleteButton, 0, wx.LEFT|wx.TOP|wx.BOTTOM, 15)
        bottomSizer.Add((5, 0))
        bottomSizer.Add(self.exportButton, 0, wx.TOP|wx.RIGHT|wx.BOTTOM, 15)
        bottomSizer.Add((0, 0), 1, wx.EXPAND, 0)
        bottomSizer.Add(self.closeButton, 0, wx.ALL, 15)
        mainSizer.Add(bottomSizer, 0, wx.EXPAND, 0)

        self.panel.SetSizer(mainSizer)
        frameSizer.Add(self.panel, 1, wx.EXPAND)
        self.SetSizer(frameSizer)
        mainSizer.Layout()
        frameSizer.Layout()


    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
    def GetListCtrl(self):
        return self.libraryList

    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
    def GetSortImages(self):
        return (self.sm_dn, self.sm_up)


    def OnUpdateUI(self, event):

        if self.inDialog:
            event.Skip()
            return

        event.Enable(self.libraryList.GetSelectedItemCount() > 0)

    
    def OnDelete(self, event=None):

        self.inDialog = True
        
        msg = "Are you sure you want to remove the selected libraries from the NDT database?"
        msg += "\n\nPlease note that this action can not be undone.\n"
        answer = self.MainFrame.RunError("Question", msg)

        self.inDialog = False

        if answer != wx.ID_YES:
            self.SetFocus()
            return

        indexes = []
        index = self.libraryList.GetFirstSelected()
        while index != -1:
            text = self.libraryList.GetItemText(index)
            indexes.append((index, text))
            index = self.libraryList.GetNextSelected(index)
        
        indexes.reverse()

        database = self.MainFrame.database
        
        wx.BeginBusyCursor()
        
        for index, name in indexes:
            self.MainFrame.SendMessage('Message', "Deleting library '%s' from database..."%name)
            self.libraryList.DeleteItem(index)
            database.DeletePackage(name, vacuum=False)
            self.MainFrame.RemoveActionMenus(name)
            self.MainFrame.SendMessage('Message', "Library '%s' removed from database..."%name)

        database.Vacuum()            
        database.PopulateInputPanel()
        wx.EndBusyCursor()
        
        
    def OnClose(self, event):
        
        self.Destroy()
        

    def OnExport(self, event=None):

        data = ""

        columns = self.libraryList.GetColumnCount()
        for indx in xrange(columns):
            data += "\t%s"%self.libraryList.GetColumn(indx).GetText()

        data += "\n"
        for indx in xrange(self.libraryList.GetItemCount()):
            for jndx in xrange(columns):
                data += "\t%s"%self.libraryList.GetItem(indx, jndx).GetText()
            data += "\n"

        if wx.TheClipboard.Open():
            try:
                wx.TheClipboard.Clear()
                wx.TheClipboard.SetData(wx.TextDataObject(data))
                wx.TheClipboard.Flush()
            finally:
                wx.TheClipboard.Close()
                self.MainFrame.SendMessage("Message", "NDT summary list exported to the clipboard")
        else:
            self.MainFrame.RunError("Error", "Unable to get access to the clipboard")

            
    def OnCharHook(self, event):

        keyCode = event.GetKeyCode()
        controlDown = event.CmdDown()
        char = event.GetUnicodeKey()

        if keyCode == wx.WXK_ESCAPE:
            self.Close()
        elif controlDown:
            if char == ord("D") and self.libraryList.GetSelectedItemCount() > 0:
                self.OnDelete()
            elif char == ord("E"):
                self.OnExport()


if wx.Platform == "__WXMSW__":
##if 0:
    class BaseDiffPage(iewin.IEHtmlWindow):

        def __init__(self, parent):        

            iewin.IEHtmlWindow.__init__(self, parent, style=wx.NO_FULL_REPAINT_ON_RESIZE)
            font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
            font.SetFaceName("Courier New")
            font.SetPointSize(font.GetPointSize()-2)
            self.SetFont(font)

else:

    class BaseDiffPage(wx.html.HtmlWindow):

        def __init__(self, parent):
            
            wx.html.HtmlWindow.__init__(self, parent, style=wx.NO_FULL_REPAINT_ON_RESIZE)
            self.SetStandardFonts(-1, "Courier New", "Courier New")


class DiffPage(BaseDiffPage):

    def __init__(self, parent, klass1, klass2, treeItem):

        BaseDiffPage.__init__(self, parent)
        
        self.treeItem = treeItem
        self.klass = klass1
        
        fromlines = to_html(klass1).split('\n')
        tolines = to_html(klass2).split('\n')

        source1, source2 = GetTopLevelParent(klass1), GetTopLevelParent(klass2)

        diff = difflib.HtmlDiff().make_file(fromlines, tolines, source1.name, source2.name)
        newdiff = []
        for line in diff.split("\n"):
            if "table.diff" in line:
                line = 'table.diff {font-family:Courier; border:medium; font-size:small}'

            newdiff.append(line)
                
        tempFileName = tempfile.mktemp(".html")
        fid = open(tempFileName,'wb')
        fid.writelines("\n".join(newdiff))
        fid.close()

        if wx.Platform == "__WXMSW__":
##        if 0:
            self.Navigate(tempFileName)
        else:
            self.LoadFile(tempFileName)

##        try:
##            os.remove(tempFileName)
##        except OSError:
##            pass
        
        
    def ResizeWidgets(self):

        pass


class LegendPanel(wx.Panel):

    def __init__(self, parent):

        wx.Panel.__init__(self, parent)

        self.SetBackgroundColour(wx.WHITE)
        keys = _treeActions.keys()
        keys.sort()

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        flexSizer = wx.FlexGridSizer(len(keys), 2, 10, 7)

        for indx, key in enumerate(keys):
            
            setter, colour, description = _treeActions[key]

            if not colour:
                colour = wx.BLACK
                
            text1 = wx.StaticText(self, -1, "Example %d"%(indx+1))
            method = ("Background" in setter and ["SetBackgroundColour"] or ["SetForegroundColour"])[0]
            eval("text1.%s(%s)"%((method, colour)))

            if "Background" not in setter:
                text1.SetBackgroundColour(wx.WHITE)
                
            text2 = wx.StaticText(self, -1, " "*3 + description + " "*10)
            text2.SetBackgroundColour(wx.WHITE)

            flexSizer.Add(text1, 0, wx.ALIGN_CENTER_VERTICAL)
            flexSizer.Add(text2, 1, wx.ALIGN_CENTER_VERTICAL)

        flexSizer.AddGrowableCol(1)
        mainSizer.Add(flexSizer, 1, wx.EXPAND|wx.ALL, 5)
        self.SetSizer(mainSizer)
        mainSizer.Layout()

        self.flexSizer = flexSizer        

        self.Bind(wx.EVT_PAINT, self.OnPaint)	
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        

    def OnPaint(self, event):
        """
        Handles the ``wx.EVT_PAINT`` event for `LegendPanel`.

        :param `event`: a `wx.PaintEvent`.
        """
        
        dc = wx.BufferedPaintDC(self)
        dc.Clear()

        rowPos = colPos = 0

        rowHeights = self.flexSizer.GetRowHeights()
        colWidths = self.flexSizer.GetColWidths()

        size = self.GetClientSize()
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(wx.Pen(wx.Colour(200, 200, 200)))

        for rowH in rowHeights:
            rowPos += rowH + 10
            dc.DrawLine(0, rowPos, size.x, rowPos)

        for colW in colWidths:
            colPos += colW + 10
            dc.DrawLine(colPos, 0, colPos, rowPos)


    def OnSize(self, event):
        """
        Handles the ``wx.EVT_SIZE`` event for `LegendPanel`.

        :param `event`: a `wx.SizeEvent`.
        """

        event.Skip()
        self.Refresh()


    def OnErase(self, event):
        """
        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for `LegendPanel`.

        :param `event`: a `wx.EraseEvent`.

        :note: This method is intentionally empty, to reduce flicker.
        """

        pass


class IgnoreDialog(BaseDialog):
    
    def __init__(self, parent, mode):

        size = wx.GetApp().GetPreference("IGNORE_DIALOG_SIZE", default=wx.DefaultSize)
        pos = wx.GetApp().GetPreference("IGNORE_DIALOG_POS", default=wx.DefaultPosition)

        if size[0] < 50 or size[1] < 50:
            size = wx.DefaultSize

        BaseDialog.__init__(self, parent, size=size, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)

        if pos[0] > -10 and pos[1] > -10:
            self.SetPosition(pos)
        else:
            self.CenterOnScreen()

        self.mode = mode

        self.caption = AutoWrapStaticText(self, "In the list below, differences that are *unchecked* will be ignored in comparison mode:")
        self.ignoreListBox = ignoreListBox = wx.CheckListBox(self)
        ignoreListBox.SetMinSize((-1, 400))

        keys = _treeActions.keys()
        keys.sort()

        for indx, key in enumerate(keys[1:]):
            
            setter, colour, description = _treeActions[key]
            ignoreListBox.Append(description)

            if not colour:
                colour = wx.BLACK
                
            method = ("Background" in setter and ["SetItemBackgroundColour"] or ["SetItemForegroundColour"])[0]
            eval("ignoreListBox.%s(%d, %s)"%((method, indx, colour)))

            if key not in mode.ignoreList:
                ignoreListBox.Check(indx)

        self.CreateButtons()
        self.SetProperties("NDT: Ignore differences")
        self.DoLayout()
        self.BindEvents()


    def DoLayout(self):

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        bottomSizer = wx.BoxSizer(wx.HORIZONTAL)
        mainSizer.Add((0, 5), 0, 0, 0)

        boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        boldFont.SetWeight(wx.BOLD)
        self.caption.SetFont(boldFont)

        mainSizer.Add(self.caption, 0, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(self.ignoreListBox, 1, wx.ALL|wx.EXPAND, 5)

        bottomSizer.Add(self.okButton, 0, wx.ALL, 15)
        bottomSizer.Add((20, 20), 1, wx.EXPAND, 0)
        bottomSizer.Add(self.cancelButton, 0, wx.ALL, 15)
        mainSizer.Add(bottomSizer, 0, wx.EXPAND, 0)
        
        self.SetSizer(mainSizer)
        mainSizer.Layout()

        self.SendSizeEvent()        


    def GetIgnoreList(self):

        app = wx.GetApp()
        app.SetPreference("IGNORE_DIALOG_SIZE", self.GetSize())
        app.SetPreference("IGNORE_DIALOG_POS", self.GetPosition())

        new = []

        for indx in xrange(self.ignoreListBox.GetCount()):
            if not self.ignoreListBox.IsChecked(indx):
                new.append(indx+1)

        return new


class DiagramPanel(wx.ScrolledWindow):
    
    def __init__(self, parent, pngFile):

        wx.ScrolledWindow.__init__(self, parent, style=wx.SUNKEN_BORDER)

        self.MainFrame = wx.GetTopLevelParent(self)

        self.SetBackgroundColour(wx.WHITE)
        bmp = self.bmp = wx.Bitmap(pngFile, wx.BITMAP_TYPE_PNG)
        self.pngFile = pngFile

        self.maxWidth, self.maxHeight = bmp.GetWidth(), bmp.GetHeight()
        self.SetVirtualSize((self.maxWidth, self.maxHeight))
        self.SetScrollRate(20, 20)

        self.popupID = wx.NewId()
        self.mouseChanged = False

        self.ReadMapFile()

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
        self.Bind(wx.EVT_MENU, self.OnExport, id=self.popupID)
        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
        self.Bind(wx.EVT_MOTION, self.OnMove)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)

        if wx.Platform == "__WXMSW__":
            self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown)


    def ReadMapFile(self):

        mapFile = self.mapFile = self.pngFile + ".map"
        rects = {}

        fid = open(mapFile, "rt")

        while 1:
            tline = fid.readline()
            if not tline:
                fid.close()
                break

            if tline.strip() and 'coords' in tline:
                tl = tline.split()
                href, coords = tl[3], tl[-1]
                coords = coords.replace('coords="', "").replace('"', '').replace("/>", "").split(",")
                href = href.replace('href="', "").replace('"', '').replace("/>", "")
                
                numbers = [int(num) for num in coords]
                x1, y1, x2, y2 = numbers
                rect = wx.Rect(x1, y1, x2-x1+1, y2-y1+1)
                rects[rect] = href

        self.rects = rects
        

    def OnPaint(self, event):
        
        dc = wx.PaintDC(self)
        self.PrepareDC(dc)

        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.Clear()

        dc.DrawBitmap(self.bmp, 0, 0, True)


    def OnRightUp(self, event):

        # make a menu
        menu = wx.Menu()
        # Show how to put an icon in the menu
        item = wx.MenuItem(menu, self.popupID, "Export image...")
        item.SetBitmap(self.MainFrame.CreateBitmap("export"))
        menu.AppendItem(item)

        # Popup the menu.  If an item is selected then its handler
        # will be called before PopupMenu returns.
        self.PopupMenu(menu)
        menu.Destroy()


    def HitTest(self, pos):

        pos = self.CalcUnscrolledPosition(pos)

        for rects, hrefs in self.rects.items():
            if rects.Contains(pos):
                return hrefs

        return None
    

    def OnMove(self, event):

        href = self.HitTest(event.GetPosition())

        if href is not None:        
            self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
            self.mouseChanged = True
            self.SetToolTipString("Left click will locate this class in the left tree")
            return

        if self.mouseChanged:
            self.SetCursor(wx.NullCursor)
            self.SetToolTipString("")
            self.mouseChanged = False


    def OnLeftDown(self, event):

        href = self.HitTest(event.GetPosition())

        if href is not None:
            self.MainFrame.dbTree.Freeze()
            self.MainFrame.dbTree.FindClass(href)
            self.MainFrame.dbTree.Thaw()
                

    def OnExport(self, event):

        dlg = wx.FileDialog(self, message="Please select the PNG output file...",
                            defaultFile="inheritance.png", wildcard="PNG files (*.png)|*.png",
                            style=wx.FD_SAVE|wx.FD_CHANGE_DIR)

        dlg.CenterOnParent()
        
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            dlg.Destroy()
            try:
                shutil.copyfile(self.pngFile, path)
                self.MainFrame.SendMessage("Message", "Inheritance diagram successfully saved to %s"%path)
            except OSError, err:
                self.MainFrame.SendMessage("Error", "Unable to save inheritance diagram PNG file")
                self.MainFrame.SendMessage("Error", "Reported exception was: %s"%err)
        else:
            dlg.Destroy()        
        

    def OnDestroy(self, event):

        try:
            os.remove(self.pngFile)
            os.remove(self.mapFile)
        except OSError:
            self.MainFrame.SendMessage("Warning", "Unable to remove temporary PNG file: %s"%self.pngFile)

        event.Skip()            

    
class TransientPopup(STT.SuperToolTip):
    """
    This is a sublass of L{SuperToolTip} and it is used to display a
    "comment-window" on the cells containing a comment (a note).
    """
    
    def __init__(self, window, treeItem):
        """
        Default class constructor.

        :param `window`: the actual window representing the tree control;
        :param `treeItem`: a tree control item.
        """

        focus = wx.Window.FindFocus()
        
        STT.SuperToolTip.__init__(self, window)
        self.SetTarget(window)

        self.MainFrame = wx.GetTopLevelParent(window)
        focus.SetFocus()


    def FormatTipWindow(self, item):

        data = item.GetData()

        if isinstance(data, list):
    
            klass1, klass2 = data

            diffs = 'None'
            if item.diffs >= 0 and item.diffs != '':
                action, colour, diffs = _treeActions[treeItem.diffs]
            
            strs = "Name: %s\nType: %s\n\nDifference: %s"%(klass1.name, klass1.obj_type, diffs)
            
        else:
            
            klass1 = data
        
            direct = klass1.GetChildrenCount(False)
            total = klass1.GetChildrenCount()

            strs = "Name: %s\nType: %s\nInherited: %s\n\nDirect children: %d\nTotal descendants: %d"% \
                   (klass1.name, klass1.obj_type, klass1.is_redundant, direct, total)

        bitmap = self.MainFrame.CreateBitmap(object_types.whatis(klass1.kind).lower())

        self.SetHeader(klass1.GetShortName())
        self.SetHeaderBitmap(bitmap)
        self.SetMessage(strs)
        self.SetDrawHeaderLine(True)

        self.SetStartDelay(100000)
        self.SetEndDelay(100000)
        self.ApplyStyle("Office 2007 Blue")
        
        self.SetDropShadow(True)
        self.DoShowNow()

        self.GetTipWindow().SetPosition(wx.GetMousePosition())
        